package com.giacom.docx4j.plugin.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.text.WordUtils;

import com.giacom.document.CtColunaTabela;
import com.giacom.document.CtLinhaTabela;
import com.giacom.document.CtPlugin;
import com.giacom.document.CtPlugin.Parametro;
import com.giacom.document.CtSecao;
import com.giacom.document.CtTabela;
import com.giacom.document.Documento;
import com.giacom.docx4j.motor.Resultado;
import com.giacom.docx4j.plugin.IMainDocumentPlugin;

/**
 * Plugin para geracao de scripts SQL
 * 
 * @author sandro.giacomozzi
 *
 */
public class ScriptGeneratorPlugin implements IMainDocumentPlugin {

	
	private String[] entityNameP = null;
	private String[] entityFieldNameP = null;
	private String[] entityFieldTypeP = null;
	private String[] entityFieldSizeP = null;
	private String[] entityFieldRequiredP = null;
	private String[] entityFieldDescriptionP = null;
	private String[] entityFieldRestartP = null;

	
	@Override
	public void execute(CtPlugin plugin,  Documento templateXML, Documento runtimeXML)
			throws Exception {
		
		//1 - identifica as entidades que devem ser geradas
		
		Map<String, String> param = new HashMap<String, String>();
		
		//seta os parametros
		List<Parametro> p = plugin.getParametro();
		for (Parametro parametro : p) {
			param.put(parametro.getNome(), parametro.getValor());
		}
		
		
		//obtem a sessao onde esta a definicao das tabelas
		int sess = Integer.parseInt(param.get("sessao"));
		CtSecao secao = null; 
				
		List<CtSecao> lsSecao = runtimeXML.getCorpo().getSecao();
		for (CtSecao ctSecao : lsSecao) {
			if(ctSecao.getIdSecao() == sess){
				secao = ctSecao;
				break;
			}
		}
		
		if(secao == null){
			throw new Exception("Nao existe secao com definicao de tabela.");
		}
		
		setInitParams(param);
		
		//lista as tabelas da sessao configurada
		List<CtTabela> tab = secao.getTabela();
		
		//procura o nome da entidade
		String entityName = null;
		boolean exit;
		
		for (CtTabela t : tab) {
			exit = false;
			if(t.getIdTabela() == Integer.parseInt(entityNameP[0])){
				for(CtLinhaTabela l: t.getLinhaTabela()){
					if(l.getIdLinhaTabela() == Integer.parseInt(entityNameP[1])){
						for(CtColunaTabela c: l.getColunaTabela()){
							if(c.getIdColunaTabela() == Integer.parseInt(entityNameP[2])){
								entityName = WordUtils.capitalizeFully(c.getTextoColunaTabela());
								//obtem os campos
								generateFields(entityName, t, param);
								//reiniciar os valores
								setInitParams(param);
								//atualiza o ID da tabela para a proxima
								updateTableInitParams(param);
								//deve ir para a proxima tabela
								exit = true;
								break;
							}
							if(exit) break;
						}
						if(exit) break;
					}
				}
			}
			
		}
		
		
	}

	private void setInitParams(Map<String, String> param) {
		//dados da primeira entidade e conjunto de campos
		entityNameP = param.get("entity.name").split(";");
		entityFieldNameP = param.get("entity.field.name").split(";");
		entityFieldTypeP = param.get("entity.field.type").split(";");
		entityFieldSizeP = param.get("entity.field.size").split(";");
		entityFieldRequiredP = param.get("entity.field.required").split(";");
		entityFieldDescriptionP = param.get("entity.field.description").split(";");
		entityFieldRestartP = param.get("entity.field.restart").split(";");
	}

	private void updateTableInitParams(Map<String, String> param) {
		//dados da primeira entidade e conjunto de campos
		entityNameP = param.get("entity.name").split(";");
		entityFieldNameP = param.get("entity.field.name").split(";");
		entityFieldTypeP = param.get("entity.field.type").split(";");
		entityFieldSizeP = param.get("entity.field.size").split(";");
		entityFieldRequiredP = param.get("entity.field.required").split(";");
		entityFieldDescriptionP = param.get("entity.field.description").split(";");
		entityFieldRestartP = param.get("entity.field.restart").split(";");
		
		
		entityNameP = new String[]{String.valueOf(Integer.parseInt(entityNameP[0]) + 1)
				,entityNameP[1]
				,entityNameP[2]};
		entityFieldNameP = new String[]{String.valueOf(Integer.parseInt(entityFieldNameP[0]) + 1)
				,entityFieldNameP[1]
				,entityFieldNameP[2]};
		entityFieldTypeP = new String[]{String.valueOf(Integer.parseInt(entityFieldTypeP[0]) + 1)
				,entityFieldTypeP[1]
				,entityFieldTypeP[2]};
		entityFieldSizeP = new String[]{String.valueOf(Integer.parseInt(entityFieldSizeP[0]) + 1)
				,entityFieldSizeP[1]
				,entityFieldSizeP[2]};
		entityFieldRequiredP = new String[]{String.valueOf(Integer.parseInt(entityFieldRequiredP[0]) + 1)
				,entityFieldRequiredP[1]
				,entityFieldRequiredP[2]};
		entityFieldDescriptionP = new String[]{String.valueOf(Integer.parseInt(entityFieldDescriptionP[0]) + 1)
				,entityFieldDescriptionP[1]
				,entityFieldDescriptionP[2]};
		entityFieldRestartP = new String[]{String.valueOf(Integer.parseInt(entityFieldRestartP[0]) + 1)
				,entityFieldRestartP[1]
				,entityFieldRestartP[2]};

	}

	
	/**
	 * Gera os atributos da entidade e envia para geracao e compilacao da entidade e geracao do script sql
	 * @param entityName
	 * @param t
	 * @param param
	 * @throws Exception
	 */
	private void generateFields(String entityName, CtTabela t, Map<String, String> param) throws Exception {

		//para cada entidade tambem deve ter um while para o conjunto de campos
		
		List<EntityField> fields = new ArrayList<EntityField>();
		
		//monta os campos da tabela
		
		EntityField f = new EntityField();
		
			if(t.getIdTabela() == Integer.parseInt(entityFieldNameP[0])){
				for(CtLinhaTabela l: t.getLinhaTabela()){
					//nome do campo
					if(l.getIdLinhaTabela() == Integer.parseInt(entityFieldNameP[1])){
						for(CtColunaTabela c: l.getColunaTabela()){
							if(c.getIdColunaTabela() == Integer.parseInt(entityFieldNameP[2])){
								f.setName(c.getTextoColunaTabela().toLowerCase());
								break;
							}
						}
					}
					//tipo do campo
					if(l.getIdLinhaTabela() == Integer.parseInt(entityFieldTypeP[1])){
						for(CtColunaTabela c: l.getColunaTabela()){
							if(c.getIdColunaTabela() == Integer.parseInt(entityFieldTypeP[2])){
								f.setType(c.getTextoColunaTabela());
								break;
							}
						}
					}
					//tamanho do campo
					if(l.getIdLinhaTabela() == Integer.parseInt(entityFieldSizeP[1])){
						for(CtColunaTabela c: l.getColunaTabela()){
							if(c.getIdColunaTabela() == Integer.parseInt(entityFieldSizeP[2])){
								f.setSize(c.getTextoColunaTabela());
								break;
							}
						}
					}
					//obrigatoriedade do campo
					if(l.getIdLinhaTabela() == Integer.parseInt(entityFieldRequiredP[1])){
						for(CtColunaTabela c: l.getColunaTabela()){
							if(c.getIdColunaTabela() == Integer.parseInt(entityFieldRequiredP[2])){
								boolean required = (c.getTextoColunaTabela().equals("N")? false : true);
								f.setRequired(required);
								break;
							}
						}
					}
					//descricao do campo
					if(l.getIdLinhaTabela() == Integer.parseInt(entityFieldDescriptionP[1])){
						for(CtColunaTabela c: l.getColunaTabela()){
							if(c.getIdColunaTabela() == Integer.parseInt(entityFieldDescriptionP[2])){
								f.setDescription(c.getTextoColunaTabela());
								break;
							}
						}
					}
					
					//verifica se esta na ultima linha antes do reinicio dos campos
					if(l.getIdLinhaTabela() == Integer.parseInt(entityFieldRestartP[1])-1){
						//inicia novos campos
						fields.add(f);
						
						//atualiza os arrays dos campos
						
						entityFieldNameP = new String[]{entityFieldRestartP[0]
								,entityFieldRestartP[1]
								,entityFieldNameP[2]};
						
						entityFieldTypeP = new String[]{entityFieldRestartP[0],
								String.valueOf(Integer.parseInt(entityFieldRestartP[1]) + 1)
								,entityFieldTypeP[2]};
						
						entityFieldSizeP = new String[]{entityFieldRestartP[0],
								String.valueOf(Integer.parseInt(entityFieldRestartP[1]) + 2)
								,entityFieldSizeP[2]};
						
						entityFieldRequiredP =  new String[]{entityFieldRestartP[0],
								String.valueOf(Integer.parseInt(entityFieldRestartP[1]) + 3)
								,entityFieldRequiredP[2]};
						
						entityFieldDescriptionP =new String[]{entityFieldRestartP[0],
								String.valueOf(Integer.parseInt(entityFieldRestartP[1]) + 4)
								,entityFieldDescriptionP[2]};
						
						
						//atualiza a variavel entityFieldRestartP para a proxima interacao
						entityFieldRestartP = new String[]{
								 entityFieldRestartP[0] //aqui deve ter a informacao da tabela atualizada
								,String.valueOf((Integer.parseInt(entityFieldRestartP[1])) + 5)
								,entityFieldRestartP[2]};
						
						f = new EntityField();
						
					}
				}
				
			}
		
		//2 - gera a entidade
		EntityGenerator e = new EntityGenerator();
		String entityGen = e.generate(entityName, fields);
		
		//3 - compila as classes
		ClassCompiler c = new ClassCompiler();
		c.compileClass("./gen/" + entityName + ".java");
		
		//4 - executa a geracao dos scripts
		DDLGenerator g = new DDLGenerator();
		String ddl = g.generate(param.get("ddl.dialect"), new String[]{ entityName });
		
		//5 - mosta o resultado
		Resultado res = Resultado.getInstance();
		
		//entidade gerada
		res.adicionaResultado(Resultado.TIPO.PLUGIN,
				Resultado.TIPO_RESULTADO.SUCESSO, "Entidade gerada: " + entityGen);

		//script gerada
		res.adicionaResultado(Resultado.TIPO.PLUGIN,
				Resultado.TIPO_RESULTADO.SUCESSO, "DDL gerada: " +	ddl);
		
		
	}

}
